package no.rodland.acc.service;

import javax.sql.DataSource;

import org.apache.log4j.Logger;
import org.joda.time.DateTime;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

import no.rodland.acc.db.DbRow;
import no.rodland.acc.db.DbUtil;
import no.rodland.acc.db.FetchService;
import no.rodland.acc.db.SqlGenerator;
import no.rodland.acc.db.StoreService;
import no.rodland.acc.model.Bank;
import no.rodland.acc.model.Budget;
import no.rodland.acc.model.RowMappers;

/**
 * Created by IntelliJ IDEA.
 *
 * @Since: Jun 18, 2010 10:47:53 AM
 * @Author: fmr
 */
class StoreServiceImpl implements StoreService {
    private static final Logger log = Logger.getLogger(StoreServiceImpl.class);

    private final JdbcTemplate template;
    private RowMappers rowMappers;
    private FetchService cachedService;

    protected StoreServiceImpl(DataSource ds, FetchService cachedService, RowMappers rowMappers) {
        template = new JdbcTemplate(ds);
        this.rowMappers = rowMappers;
        this.cachedService = cachedService;
    }

    @Override
    public <T extends DbRow> boolean store(T... row) {
        for (T t : row) {
            T result = store(t);
        }
        return true;
    }

    @SuppressWarnings({"unchecked"})
    @Override
    public <T extends DbRow> T store(T row) {
        if (row == null) {
            throw new IllegalArgumentException("row cannot be null");
        }
        RowMapper<T> rowMapper = rowMappers.getRowMapper(row);
        String sql = row.isNew() ? row.getInsertSql() : row.getUpdateSql();
        boolean inserted = (1 == (template.update(sql, row.getData())));
        int id = row.isNew() ? getLastId() : row.getId();
        return getRowById(row, id, rowMapper);
        //throw new UnsupportedOperationException(row.getTableName() + " is not supported for " + (row.isNew() ? "insert" : "update"));
    }

    @Override
    public <T extends DbRow> int insert(List<T> rows) {
        if (rows == null || rows.size() == 0) {
            return 0;
        }
        int updated = 0;
        Set<Bank> banks = null;
        for (T row : rows) {
            boolean duplicate = false;
            if (row instanceof Bank) {
                Bank bank = (Bank) row;
                if (banks == null) {
                    DateTime firstDate = DbUtil.getFirstDate(rows);
                    banks = new HashSet<Bank>(cachedService.getBanks(firstDate).values());
                }
                if (banks.contains(bank)) {
                    duplicate = true;
                }
            }
            if (!duplicate) {
                updated += template.update(row.getInsertSql(), row.getData());
            }
            else {
                log.warn("Already in DB - not inserted: " + row);
            }
        }
        if (updated != rows.size()) {
            log.warn("hm - only inserted " + updated + " rows out of " + rows.size());
        }
        return updated;
    }

    @Override
    public boolean match(Bank bank, Budget budget) {
// XXX implement
//        if (budget == null){
//            budget = new Budget(bank);
//        }
//        updateBudgetFromBank(bank, budget);
//        BudgetBank budgetBank = new BudgetBank(bank, budget);
//        insert(budgetBank);
//        store(bank, budget);
        return false;
    }

    private <T extends DbRow> T getRowById(DbRow row, int id, RowMapper<T> rowMapper) {
        final String tableName = row.getTableName();
        String sql = SqlGenerator.getById(id, tableName);
        return template.query(sql, rowMapper).get(0);
    }

    private int getLastId() {
        return template.queryForInt(SqlGenerator.getLastInsertId());
    }
}
